//
//  IAAny.h
//  OperatorTypeTest
//
//  Created by Christopher Kemsley on 1/29/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#ifndef _IAAny
#define _IAAny

#include <typeinfo>







/*** Ignore this ***/

struct	Any ;

class	AnyObj
{
protected:
	friend struct Any ;
	
	virtual ~AnyObj () { }
	
	virtual std::type_info const &	type () const			= 0 ;
	virtual std::type_info const &	instanceType () const	= 0 ;
	virtual AnyObj*					copy () const			= 0 ;
} ;

template <typename Type>
class	TAnyObj : public AnyObj
{
protected:
	friend struct Any ;
	
	Type					value ;
	std::type_info const&	mType ;
	std::type_info const&	mInstanceType ;
	
	TAnyObj ( Type const & inValue )
	:	value			( inValue )
	,	mType			( typeid(Type) )
	,	mInstanceType	( typeid(inValue) )
		{
		}
	
	
	virtual std::type_info const &	type () const
		{
		return mType ;
		}
	virtual std::type_info const &	instanceType () const
		{
		return mInstanceType ;
		}
	virtual AnyObj*					copy () const
		{
		return new TAnyObj<Type> ( value ) ;
		}
} ;













/*** Any class ***/


struct	Any
{
	
	/** Construction & Destruction **/
	
	Any ()						: mObject ( 0 ) { }
	Any ( Any const& c )		: mObject ( c.mObject==0 ? 0 : c.mObject->copy() ) { }
	
	template <typename Type>
	Any ( Type const& c )		: mObject ( new TAnyObj<Type> ( c ) ) { }
	
	~Any ()						{ if(mObject!=0) delete mObject ; }
	
	
	
	/** Setting **/
	
	template <typename Type>
	Any&		operator=	( Type const& value )
		{
		if ( mObject != 0 )
			delete mObject ;
		mObject = new TAnyObj<Type> ( value ) ;
		return *this ;
		}
	Any&		operator=	( Any const& value )
		{
		if ( mObject != 0 )
			delete mObject ;
		mObject = value.mObject -> copy () ;
		return *this ;
		}
	template <typename Type>
	Any&		set			( Type const& value )
		{
		if ( mObject != 0 )
			delete mObject ;
		mObject = new TAnyObj<Type> ( value ) ;
		return *this ;
		}
	
	
	
	/** Querying **/
	
	bool		hasValue () const
		{
		return mObject != 0 ;
		}
	
	
	
	/** Getting types **/
	
	std::type_info const &	type () const
		{
		if ( mObject == 0 )
			return typeid(void) ;
		return mObject -> type () ;
		}
	std::type_info const &	instanceType () const
		{
		if ( mObject == 0 )
			return typeid(void) ;
		return mObject -> instanceType () ;
		}
	
	
	
	/** Casting **/
	
	template <typename Type>
	Type&			as () // safe cast
		{
		return this -> castSafely<Type> () ;
		}
	template <typename Type>
	Type const&		as () const // safe cast
		{
		return this -> castSafely<Type> () ;
		}
	
	template <typename Type>
	Type&			cast ()
		{
		if ( mObject )
			return ( static_cast < TAnyObj<Type>* > ( mObject ) -> value ) ;
		return *(Type*)0 ;
		}
	template <typename Type>
	Type const&		cast () const
		{
		if ( mObject )
			return ( static_cast < TAnyObj<Type>* > ( mObject ) -> value ) ;
		return *(Type*)0 ;
		}
	
	template <typename Type>
	Type&			castSafely ()
		{
		if ( mObject )
			{
			if (!( typeid(Type) == mObject->type() )) throw "Any: bad cast" ;
			return ( static_cast < TAnyObj<Type>* > ( mObject ) -> value ) ;
			}
		return *(Type*)0 ;
		}
	template <typename Type>
	Type const&		castSafely () const
		{
		if ( mObject )
			{
			if (!( typeid(Type) == mObject->type() )) throw "Any: bad cast" ;
			return ( static_cast < TAnyObj<Type>* > ( mObject ) -> value ) ;
			}
		return *(Type*)0 ;
		}
	
	template <typename Type>
	operator		Type* () // unsafe cast
		{
		return & (*this) . cast<Type> () ;
		}
	template <typename Type>
	operator		Type const* () const // unsafe cast
		{
		return & (*this) . cast<Type> () ;
		}
	
	
private:
	AnyObj * mObject ;
} ;








#endif // ifndef _IAAny
